﻿namespace BadHabit.XPSTools
{
    using System;
    using System.IO;
    using System.Windows.Documents;
    using System.Windows.Markup;
    using System.Windows.Xps.Packaging;
    using System.Windows.Xps.Serialization;
    using System.Xml.XPath;
    using System.Xml.Xsl;

    public static class XPSHelper
    {
        /// <summary>
        /// The write mode for creating a XPS file.
        /// </summary>
        public enum XPSWriteMode
        {
            Overwrite,
            Append
        }

        /// <summary>
        /// Creates a xps file from a xaml stream.
        /// </summary>
        /// <param name="srcXamlStream">Xaml stream</param>
        /// <param name="context">Context</param>
        /// <param name="destXpsFile">XPS file path</param>
        /// <param name="writeMode">The writemode</param>
        public static void XamlStringToXps(string xamlText, ParserContext context, string destXpsFile, XPSWriteMode writeMode, bool isFlowDocument)
        {
            XpsDocument document;
            if (writeMode.Equals(XPSWriteMode.Overwrite))
            {
                document = new XpsDocument(destXpsFile, FileAccess.Write);
            }
            else
            {
                document = new XpsDocument(destXpsFile, FileAccess.ReadWrite);
            }

            XpsPackagingPolicy packagePolicy = new XpsPackagingPolicy(document);
            XpsSerializationManager serializationMgr = new XpsSerializationManager(packagePolicy, false);

            if (isFlowDocument)
            {
                IDocumentPaginatorSource text = (null == context) ?
                    XamlReader.Parse(xamlText) as IDocumentPaginatorSource :
                    XamlReader.Parse(xamlText, context) as IDocumentPaginatorSource;
                DocumentPaginator paginator = text.DocumentPaginator;
                serializationMgr.SaveAsXaml(paginator);
            }
            else
            {
                object parsedDocObject = (null == context) ? XamlReader.Parse(xamlText) : XamlReader.Parse(xamlText, context);
                serializationMgr.SaveAsXaml(parsedDocObject);
            }

            document.Close();
        }

        public static FlowDocument CopyFlowDocument(FlowDocument flowDocument)
        {
            System.IO.MemoryStream s = new System.IO.MemoryStream();
            TextRange source = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd);
            source.Save(s, System.Windows.DataFormats.Xaml);

            FlowDocument copy = new FlowDocument();
            TextRange dest = new TextRange(copy.ContentStart, copy.ContentEnd);
            dest.Load(s, System.Windows.DataFormats.Xaml);
            return copy;
        }

        public static void FlowDocumentToXps(FlowDocument flowDocument, string destXpsFile, ParserContext context, XPSWriteMode writeMode)
        {
            XpsDocument document;
            if (writeMode.Equals(XPSWriteMode.Overwrite))
            {
                document = new XpsDocument(destXpsFile, FileAccess.Write);
            }
            else
            {
                document = new XpsDocument(destXpsFile, FileAccess.ReadWrite);
            }

            XpsPackagingPolicy packagePolicy = new XpsPackagingPolicy(document);
            XpsSerializationManager serializationMgr = new XpsSerializationManager(packagePolicy, false);
            IDocumentPaginatorSource paginatorSource = flowDocument as IDocumentPaginatorSource;

            serializationMgr.SaveAsXaml(paginatorSource.DocumentPaginator);
            document.Close();
        }

        /// <summary>
        /// Creates a xps file from a xaml stream.
        /// </summary>
        /// <param name="srcXamlStream">Xaml stream</param>
        /// <param name="context">Context</param>
        /// <param name="destXpsFile">XPS file path</param>
        /// <param name="writeMode">The writemode</param>
        public static void XamlStreamToXps(Stream srcXamlStream, ParserContext context, string destXpsFile, XPSWriteMode writeMode, bool isFlowDocument)
        {
            XpsDocument document;
            if (writeMode.Equals(XPSWriteMode.Overwrite))
            {
                document = new XpsDocument(destXpsFile, FileAccess.Write);
            }
            else
            {
                document = new XpsDocument(destXpsFile, FileAccess.ReadWrite);
            }

            XpsPackagingPolicy packagePolicy = new XpsPackagingPolicy(document);
            XpsSerializationManager serializationMgr = new XpsSerializationManager(packagePolicy, false);

            if (isFlowDocument)
            {
                IDocumentPaginatorSource text = (null == context) ? XamlReader.Load(srcXamlStream) as IDocumentPaginatorSource : XamlReader.Load(srcXamlStream, context) as IDocumentPaginatorSource;
                DocumentPaginator paginator = text.DocumentPaginator;
                serializationMgr.SaveAsXaml(paginator);
            }
            else
            {
                object parsedDocObject = (null == context) ? XamlReader.Load(srcXamlStream) : XamlReader.Load(srcXamlStream, context);
                serializationMgr.SaveAsXaml(parsedDocObject);
            }

            document.Close();
        }

        /// <summary>
        /// Creates a XPS file from a Xaml file.
        /// </summary>
        /// <param name="srcXamlFile">Xaml file path</param>
        /// <param name="destXpsFile">XPS file path</param>
        /// <param name="writeMode">Write mode</param>
        /// <param name="isFlowDocument">True to create a flow document</param>
        public static void XamlFileToXps(string srcXamlFile, string destXpsFile, string contextUri, XPSWriteMode writeMode, bool isFlowDocument)
        {
            using (Stream fileStream = File.OpenRead(srcXamlFile))
            {
                ParserContext context = new ParserContext();
                context.BaseUri = new Uri(contextUri);
                XamlStreamToXps(fileStream, context, destXpsFile, writeMode, isFlowDocument);
            }
        }

        /// <summary>
        /// Applies a XSLT transform in a XML data to generate a XPS file.
        /// </summary>
        /// <param name="xsltFile">XSLT file path</param>
        /// <param name="xmlData">XML data</param>
        /// <param name="xpsFile">XPS file path</param>
        /// <param name="contextUri">Context Uri</param>
        /// <param name="writeMode">Write Mode</param>
        /// <param name="isFlowDocument">True to create a flow document</param>
        public static void XsltTransformToXPS(string xsltFile, string xmlData, string xpsFile, string contextUri, XPSWriteMode writeMode, bool isFlowDocument)
        {
            // load the Xslt             
            XslCompiledTransform xslTrans = new XslCompiledTransform();
            xslTrans.Load(xsltFile);

            // create the output stream           
            System.IO.MemoryStream sb = new System.IO.MemoryStream();
            System.IO.StreamWriter writer = new System.IO.StreamWriter(sb);

            // do the actual transform of Xml            
            xslTrans.Transform(new XPathDocument(new System.IO.StringReader(xmlData)), null, writer);

            // creates the XPS file
            ParserContext context = new ParserContext();
            context.BaseUri = new Uri(contextUri);
            sb.Position = 0;
            XamlStreamToXps(sb, context, xpsFile, writeMode, isFlowDocument);
        }

        public static byte[] XsltTransformToXamlPackage(string xsltFile, string xmlData, string xpsFile, string contextUri, XPSWriteMode writeMode, bool isFlowDocument)
        {
            // load the Xslt             
            XslCompiledTransform xslTrans = new XslCompiledTransform();
            xslTrans.Load(xsltFile);

            // create the output stream           
            System.IO.MemoryStream sb = new System.IO.MemoryStream();
            System.IO.StreamWriter writer = new System.IO.StreamWriter(sb);

            // do the actual transform of Xml            
            xslTrans.Transform(new XPathDocument(new System.IO.StringReader(xmlData)), null, writer);

            return XamlStreamToXamlPackage(sb);
        }

        public static byte[] XamlStreamToXamlPackage(Stream stream)
        {
            FlowDocument document = XamlReader.Load(stream) as FlowDocument;
            TextRange all = new TextRange(document.ContentStart, document.ContentEnd);

            System.IO.MemoryStream sb2 = new System.IO.MemoryStream();

            all.Save(sb2, System.Windows.DataFormats.XamlPackage.ToString());

            byte[] buffer = new byte[sb2.Length];
            sb2.Read(buffer, 0, (int)sb2.Length);

            return buffer;
        }
    }
}
